package com.huiyeji.hui.service.impl;

import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
import com.huiyeji.hui.entity.*;
import com.huiyeji.hui.exception.HuiException;
import com.huiyeji.hui.properties.DefaultGroupProperties;
import com.huiyeji.hui.service.*;
import com.huiyeji.hui.utils.*;
import com.huiyeji.hui.vo.*;
import com.huiyeji.hui.ws.ChatEndpoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @author zhaoduo
 */
@Service
public class ApplicationServiceImpl implements ApplicationService {

    private final UserService userService;

    private final AliyunService aliyunService;

    private final GroupService groupService;

    private final UserRelationService userRelationService;

    private final ThreadPoolExecutor executor;

    private final DefaultGroupProperties groupProperties;

    private final GroupRelationService groupRelationService;

    private final MessageService messageService;

    private final InvitationService invitationService;

    @Autowired
    public ApplicationServiceImpl(UserService userService,
                                  AliyunService aliyunService,
                                  GroupService groupService,
                                  UserRelationService userRelationService,
                                  ThreadPoolExecutor executor,
                                  DefaultGroupProperties groupProperties,
                                  GroupRelationService groupRelationService,
                                  MessageService messageService,
                                  InvitationService invitationService) {
        this.userService = userService;
        this.aliyunService = aliyunService;
        this.groupService = groupService;
        this.userRelationService = userRelationService;
        this.executor = executor;
        this.groupProperties = groupProperties;
        this.groupRelationService = groupRelationService;
        this.messageService = messageService;
        this.invitationService = invitationService;
    }

    @Override
    public User getUserInfo(UserInfoEntity userInfo) {
        return userService.getUserInfo(userInfo);
    }

    @Override
    public void register(UserInfoEntity userInfo) {
        userInfo.setPassword(DigestUtil.bcrypt(userInfo.getPassword()));
        userService.addUser(userInfo);
    }

    @Override
    public Map<String, String> getPolicy() {
        return aliyunService.getPolicyResultMap();
    }

    @Override
    public HuiResponse<?> userBaseModify(User requestUser, User user) throws CloneNotSupportedException {
        boolean modifyOk = userService.baseModify(requestUser);
        if (modifyOk){
            user.setSex(requestUser.getSex());
            user.setProvince(requestUser.getProvince());
            user.setCity(requestUser.getCity());
            user.setRegion(requestUser.getRegion());
            user.setDetailAddress(requestUser.getDetailAddress());
            user.setEmail(requestUser.getEmail());
            user.setNickname(requestUser.getNickname());
            user.setPersonalSignature(requestUser.getPersonalSignature());
            user.setBirthday(requestUser.getBirthday());
            user.setPhone(requestUser.getPhone());
            return HuiResponse.ok(SuccessEnum.MODIFY_SUCCESS,filteringProperties(user));
        }
        return HuiResponse.error(ErrorEnum.MODIFY_ERROR);
    }



    @Override
    public void changeUserPwd(User user, PwdChangeVo vo) {
        boolean checkOldPwd = DigestUtil.bcryptCheck(vo.getOldPwd(), user.getPassword());
        if (checkOldPwd){
            userService.changePwd(user.getUid(),DigestUtil.bcrypt(vo.getNewPwd()));
        } else {
            throw new HuiException(ErrorEnum.OLD_PASSWORD_DOES_NOT_MATCH);
        }
    }

    @Override
    public SelfAndOthersAndGroupsVo getSelfAndOthersAndGroups(User user) throws CloneNotSupportedException, ExecutionException, InterruptedException {

        SelfAndOthersAndGroupsVo vo = new SelfAndOthersAndGroupsVo();

        //获得self
        vo.setSelf(filteringProperties(user));

        //准备去重用的set
        Set<Long> ids = new ConcurrentHashSet<>();

        //获得源用户所有群聊信息
        CompletableFuture<Void> groupFuture = CompletableFuture.runAsync(()->{
            List<SelfAndOthersAndGroupsVo.Group> groups = groupService.getGroups(user.getUid());
            vo.setGroups(groups);

            //抽取出所有群的所有成员id,去重
            groups.forEach(group -> ids.addAll(group.getOtherIds()));

            //去掉源用户id
            ids.remove(user.getUid());
        },executor);

        //获得源用户所有关联用户id
        CompletableFuture<Void> relationFuture = CompletableFuture.runAsync(()->{
            List<Long> relationUserIds = userRelationService.getUserRelationUserIds(user.getUid());
            ids.addAll(relationUserIds);
        },executor);

        //等待
        CompletableFuture.allOf(groupFuture,relationFuture).get();

        //获得源用户好友以及公共群成员信息
        List<SelfAndOthersAndGroupsVo.Other> others = getOthers(user.getUid(),ids);
        vo.setOthers(others);

        return vo;
    }

    @Override
    public User getUserInfoSafely(User user) throws CloneNotSupportedException {
        return filteringProperties(user);
    }

    @Override
    public void changeHeadshot(User user, String headshot) {
        userService.changeHeadshot(user,headshot);
    }

    @Override
    public void changeGroupName(Long uid, Long gid, String groupName) {
        //更改名称
        groupService.changeGroupName(uid,gid,groupName);

        //通知群聊成员
    }

    @Override
    public void changeGroupNotice(Long uid, Long gid, String notice) {
        //更改名称
        groupService.changeGroupNotice(uid,gid,notice);

        //通知群聊成员
    }

    @Override
    public PageResult<SimpleGroupVo> groupKeySearch(Long uid, Map<String, String> params) {
        PageUtil.KeyPageParam pp = PageUtil.analyzeThenBuildKeyPp(params);
        return groupService.keySearch(uid,pp);
    }

    @Override
    public PageResult<SimpleGroupVo> groupConditionSearch(Long uid, Map<String, String> params) {
        PageUtil.GroupConditionPageParam pp = PageUtil.analyzeThenBuildGroupConditionPp(params);
        return groupService.conditionSearch(uid,pp);
    }

    @Override
    public PageResult<SimpleOtherVo> userKeySearch(Long uid, Map<String, String> params) {
        PageUtil.KeyPageParam pp = PageUtil.analyzeThenBuildKeyPp(params);
        PageResult<SimpleOtherVo> result = userService.keySearch(uid, pp);
        result.getRecord().forEach(other->{
            other.setStatus(getOtherStatus(other.getOid()));
        });
        return result;
    }

    @Override
    public PageResult<SimpleOtherVo> userConditionSearch(Long uid, Map<String, String> params) {
        PageUtil.UserConditionPageParam pp = PageUtil.analyzeThenBuildUserConditionPp(params);
        PageResult<SimpleOtherVo> result = userService.conditionSearch(uid, pp);
        result.getRecord().forEach(other->{
            other.setStatus(getOtherStatus(other.getOid()));
        });
        return result;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void removeGroup(Long uid, Long gid) {
        //删除群聊
        groupService.deleteGroup(uid,gid);

        //得到群成员id集合
        List<Long> groupMembers = groupRelationService.getGroupMembers(gid, uid);

        executor.execute(()->{
            //构造消息内容
            MessageVo vo = new MessageVo(
                    gid,
                    null,
                    null,
                    LocalDateTime.now(),
                    MessageVo.Type.GROUP_REMOVE
            );

            //通知成员
            MessageUtil.groupSend(groupMembers,vo);
        });

        //删除群聊所有成员
        executor.execute(()->{
            groupRelationService.removeAllMember(gid);
        });

        //得到该群所有消息id
        List<Long> midList = messageService.getAllMessageIdOfGroup(gid);

        //删除所有群消息
        executor.execute(()->{
            messageService.deleteAllGroupMessage(gid);
        });
        executor.execute(()->{
            messageService.deleteMessages(midList);
        });

        //删除该群所有的邀请函
        executor.execute(()->{
            invitationService.deleteAllGroupInvitation(gid);
        });
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void createGroup(Long uid, String groupName) {
        //群聊表插入数据
        Group group = buildDefaultGroup();
        group.setCreateUserId(uid);
        group.setName(groupName);
        groupService.createGroup(group);

        //群聊关系表插入数据
        GroupUserRelation relation = new GroupUserRelation();
        relation.setGid(group.getGid());
        relation.setCreateTime(group.getCreateTime());
        relation.setIsCreator(new Byte("1"));
        relation.setUid(uid);
        groupRelationService.insertGroupOwner(relation);
    }



    @Override
    public void userDeleteFriend(Long uid, Long fid) {
        //用户关系表删除数据
        userRelationService.deleteFriend(uid,fid);

        //删除聊天记录
        List<Long> midList = messageService.getAllMessageIdOfPrivate(uid,fid);

        //删除私聊消息表记录
        executor.execute(()->{
            messageService.deletePrivateMessageFromTwoUser(uid,fid);
        });

        //删除消息表记录
        executor.execute(()->{
            messageService.deleteMessages(midList);
        });

        //通知
        MessageUtil.privateSend(new MessageVo(
                uid,
                fid,
                null,
                LocalDateTime.now(),
                MessageVo.Type.DELETE_FRIEND
        ));
    }

    @Override
    public void userCareFriend(Long uid, Long fid) {
        userRelationService.careFriend(uid,fid);
    }

    @Override
    public void putFriendIntoBlackList(Long uid, Long fid) {
        userRelationService.putFriendIntoBlackList(uid,fid);
    }

    @Override
    public void userChangeRemark(Long uid, Long oid, String remark) {
        userRelationService.changeRemark(uid,oid,remark);
    }

    @Override
    public void userExitGroup(Long uid, Long gid) {
        groupRelationService.removeMember(uid,gid);
        //通知
    }

    @Override
    public void kickOutOtherFromGroup(Long uid, Long gid, Long oid) {
        //从表中删除指定数据
        groupRelationService.kickOutMember(uid,gid,oid);

        executor.execute(()->{
            //得到群成员id集合
            List<Long> groupMembers = groupRelationService.getGroupMembers(gid, uid);

            //构造消息内容
            MessageVo vo = new MessageVo(
                    gid,
                    oid,
                    null,
                    LocalDateTime.now(),
                    MessageVo.Type.GROUP_KICK_OUT
            );

            //通知群成员
            MessageUtil.privateSend(oid,vo);
            MessageUtil.groupSend(groupMembers,vo);
        });
    }

    @Override
    public void userRelationRecoverNormal(Long uid, Long oid) {
        userRelationService.recoverNormalRelation(uid,oid);
    }

    @Override
    public List<SystemMessageVo> getSystemMessages() {
        return messageService.getSystemMessages();
    }

    @Override
    public List<InvitationVo> getInvitations(Long receiver){
        return invitationService.getInvitations(receiver);
    }

    @Override
    public List<GroupOrPrivateMessageVo> getGroupMessages(Long uid, Long gid) {
        return messageService.getGroupMessages(uid,gid);
    }

    @Override
    public List<GroupOrPrivateMessageVo> getPrivateMessages(Long uid, Long fid) {
        return messageService.getPrivateMessages(uid,fid);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void sendGroupMsg(MessageVo vo) {

        //插入新数据到数据表
        messageService.addGroupMsg(vo);

        //得到所有需要通知的成员id集合
        List<Long> receivers = groupRelationService.getGroupMembers(vo.getReceiver(), vo.getSender());

        //通知
        MessageUtil.groupSend(receivers,vo);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void sendPrivateMsg(MessageVo vo) {

        //插入新数据到表
        messageService.addPrivateMsg(vo);

        //通知
        MessageUtil.privateSend(vo);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void sendGroupInvitation(MessageVo vo) {

        //插入新数据到表
        List<Invitation> invitations = invitationService.addGroupInvitation(vo);

        //构造通知内容
        List<MessageVo> vos = buildGroupInvitationNoticeContent(vo, invitations);

        //通知
        MessageUtil.groupSend(vos);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void sendFriendInvitation(MessageVo vo) {

        //插入新数据到表
        Invitation invitation = invitationService.addFriendInvitation(vo);

        //构造通知内容
        buildFriendInvitationNoticeContent(vo,invitation);

        //通知
        MessageUtil.privateSend(vo);
    }

    @Override
    public void refuseInvitation(Long uid, Long id) {
        //修改邀请函状态
        invitationService.refuseInvitation(uid,id);

        //通知
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void agreeInvitation(Long uid, Long id) {
        //修改邀请函状态
        invitationService.agreeInvitation(uid,id);

        //得到该邀请函
        Invitation invitation = invitationService.getInvitation(id);

        //新增用户或群聊关系数据
        addUserOrGroupRelation(invitation);

        //通知
        MessageUtil.privateSend(new MessageVo(
                invitation.getReceiver(),
                invitation.getSender(),
                JSON.toJSONString(invitation),
                LocalDateTime.now(),
                MessageVo.Type.AGREE_INVITATION
        ));
    }

    @Override
    public SelfAndOthersAndGroupsVo.Other getOther(Long uid, Long oid) {
        SelfAndOthersAndGroupsVo.Other other = userService.getOther(uid, oid);
        if (other == null){
            throw new HuiException(ErrorEnum.NO_USER_ERROR);
        }
        other.setStatus(getOtherStatus(oid));
        return other;
    }

    @Override
    public SelfAndOthersAndGroupsVo.Group getGroup(Long uid, Long gid) {
        return groupService.getGroup(uid,gid);
    }

    @Override
    public void noticeFriends(MessageVo vo) {
        List<Long> friends = userRelationService.getUserRelationUserIds(vo.getSender());
        MessageUtil.groupSend(friends,vo);
    }

    @Override
    public void deleteInvitation(Long uid, Long id) {
        invitationService.delete(uid,id);
    }


    //--------------------------------private methods------------------------------------------

    /**
     * 过滤user敏感数据
     * @param user user
     * @return 过滤后的user
     */
    private User filteringProperties(User user) throws CloneNotSupportedException {
        User resUser = user.clone();
        resUser.setPassword(null);
        resUser.setDeleteTime(null);
        resUser.setIsDeleted(null);
        return resUser;
    }

    private List<SelfAndOthersAndGroupsVo.Other> getOthers(Long uid, Set<Long> ids){
        List<SelfAndOthersAndGroupsVo.Other> friends = userService.getOthers(uid,ids);
        friends.forEach(friend->{
            friend.setStatus(getOtherStatus(friend.getOid()));
        });
        return friends;
    }

    private Group buildDefaultGroup() {
        Group group = new Group();
        group.setName(groupProperties.getName());
        group.setHeadshot(groupProperties.getHeadshot());
        group.setGroupNotice(groupProperties.getNotice());
        group.setCreateTime(LocalDateTime.now());
        return group;
    }

    /**
     * 构造私聊邀请函通知内容
     * @param vo MessageVo
     */
    private void buildFriendInvitationNoticeContent(MessageVo vo,Invitation invitation){
        SimpleOtherVo sender = userService.getSimpleOther(vo.getSender());
        InvitationVo ivo = new InvitationVo(sender,invitation,null);
        String newContent = JSON.toJSONString(ivo);
        vo.setContent(newContent);
    }

    /**
     * 构造群聊邀请函通知内容
     * @param vo MessageVo
     * @return List<MessageVo>
     */
    private List<MessageVo> buildGroupInvitationNoticeContent(MessageVo vo,List<Invitation> invitations){
        SimpleGroupVo group = groupService.selectSimpleGroup(vo.getReceiver());
        SimpleOtherVo other = userService.getSimpleOther(vo.getSender());
        return invitations.stream().map(invitation -> {
            InvitationVo invitationVo = new InvitationVo(other, invitation, group);
            return new MessageVo(
                    invitation.getSender(),
                    invitation.getReceiver(),
                    JSON.toJSONString(invitationVo),
                    invitation.getCreateTime(),
                    vo.getType());
        }).collect(Collectors.toList());
    }

    private void addUserOrGroupRelation(Invitation invitation) {
        //好友邀请处理
        if (invitation.getType().equals(Byte.parseByte("1"))){
            UserUserRelation relation = new UserUserRelation();
            relation.setUid1(invitation.getSender());
            relation.setUid2(invitation.getReceiver());
            relation.setRelation(Byte.parseByte("2"));
            relation.setCreateTime(LocalDateTime.now());
            userRelationService.addRelation(relation);
        }

        //群邀请处理
        if (invitation.getType().equals(Byte.parseByte("2"))){
            GroupUserRelation relation = new GroupUserRelation();
            relation.setGid(invitation.getGid());
            relation.setUid(invitation.getReceiver());
            relation.setIsCreator(Byte.parseByte("0"));
            relation.setCreateTime(LocalDateTime.now());
            groupRelationService.addRelation(relation);
        }
    }

    /**
     * 得到用户状态(是否在线)
     * @param oid oid
     * @return 在线:1   不在线:0
     */
    private Byte getOtherStatus(Long oid){
        boolean isOnline = ChatEndpoint.ONLINE_USERS.containsKey(oid);
        return isOnline?Byte.parseByte("1"):Byte.parseByte("0");
    }
}
